package com.devilvan.betrice.mg.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.devilvan.betrice.common.BetriceResultCode;
import com.devilvan.betrice.common.BetriceResultVo;
import com.devilvan.betrice.exception.BetriceException;
import com.devilvan.betrice.mg.constant.MgConstant;
import com.devilvan.betrice.mg.dto.MgDetailDto;
import com.devilvan.betrice.mg.dto.MgPreviewPicDto;
import com.devilvan.betrice.mg.po.MgDetailPo;
import com.devilvan.betrice.mg.service.MgDetailService;
import com.devilvan.betrice.mg.mapper.MgHeaderMapper;
import com.devilvan.betrice.mg.repository.MgHeaderRepository;
import com.devilvan.betrice.mg.service.MgPreviewPicService;
import com.devilvan.betrice.mg.vo.MgDetailRequestVo;
import com.devilvan.betrice.request.component.JsoupComponent;
import com.devilvan.betrice.util.ExploreDriverUtil;
import com.devilvan.betrice.util.UString;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.devilvan.betrice.util.UCopy;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Evad.Wu
 * @Description 198763-明细信息表 业务逻辑类
 * @date 2023-01-26
 */
@Service
public class MgDetailServiceImpl extends ServiceImpl<MgHeaderMapper, MgDetailPo> implements MgDetailService {
    private final String pubDateStr = "【发布时间】：";
    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    private ChromeDriver chromeDriver = null;
    @Resource(name = "jsoupComponent")
    private JsoupComponent jsoupComponent;
    @Resource(name = "transactionTemplate")
    private TransactionTemplate transactionTemplate;
    @Resource(name = "betriceRedisTemplate")
    private RedisTemplate<String, Object> redisTemplate;
    @Resource(name = "threadPoolExecutor")
    private ThreadPoolExecutor threadPoolExecutor;

    @Resource(name = "mgHeaderRepository")
    private MgHeaderRepository mgHeaderRepository;
    @Resource(name = "mgPreviewPicServiceImpl")
    private MgPreviewPicService mgPreviewPicService;

    @Override
    public MgDetailPo saveUpdate(MgDetailDto mgDetailDto) {
        MgDetailPo mgDetailPo = new MgDetailPo();
        UCopy.fullCopy(mgDetailDto, mgDetailPo);
        return mgHeaderRepository.saveAndFlush(mgDetailPo);
    }

    @Override
    public List<MgDetailPo> saveUpdateBatch(List<MgDetailDto> list) {
        List<MgDetailPo> mgDetailPoList = UCopy.fullCopyCollections(list, MgDetailPo.class);
        return mgHeaderRepository.saveAllAndFlush(mgDetailPoList);
    }

    @Override
    public BetriceResultVo<MgDetailPo> doMgDetailCrawler(MgDetailRequestVo mgDetailRequestVo) {
        BetriceResultVo<MgDetailPo> betriceResultVo = new BetriceResultVo<>();
        String type = mgDetailRequestVo.getType();
        Integer[] range = mgDetailRequestVo.getRange();
        int start = -1;
        int end = -1;
        if (range != null) {
            start = range[0];
            end = range[1];
        }
        for (int i = start; i <= end; i++) {
            final int fi = i;
            threadPoolExecutor.execute(() -> {
                // params
                Map<String, Object> params = new HashMap<>(16);
                params.put("class", type);
                params.put("page", fi);
                String url = UString.generateHttpParamUrl(MgConstant.MG_HEADER_URL_PREFIX, params);
                Element body = jsoupComponent.getBody(url);
                Elements selector = body.select("div#content > div.post.post-last > ul > li > a");
                Elements elements = Optional.ofNullable(selector).orElseThrow(() -> new BetriceException("body为空！"));
                // 去除第一个和最后一个无关的URL
                elements.remove(0);
                assert elements.size() > 0;
                elements.remove(elements.size() - 1);
                List<String> urlList = new ArrayList<>();
                try {
                    for (Element element : elements) {
                        String href = MgConstant.MG_PREFIX + element.attr("href");
                        urlList.add(href);
                    }
                    redisTemplate.opsForSet().add(MgConstant.MG_LIST_KEY + type, urlList.toArray(new String[0]));
                    betriceResultVo.result(BetriceResultCode.SUCCESS_200, "操作成功！");
                    betriceResultVo.setHandleCount(urlList.size());
                } catch (Exception e) {
                    betriceResultVo.result(BetriceResultCode.FAILED_500, "操作失败！");
                }
            });
        }
        return betriceResultVo;
    }

    @Override
    public BetriceResultVo<Object> doMgDetailConsumer(String type, Integer pageSize) {
        if (chromeDriver == null) {
            chromeDriver = ExploreDriverUtil.getChromeDriver();
        }
        BetriceResultVo<Object> betriceResultVo = new BetriceResultVo<>();
        List<String> list = Collections.singletonList(Objects.requireNonNull(redisTemplate.opsForSet()
                .pop(MgConstant.MG_LIST_KEY + type, pageSize)).toString());
        List<String> pidList = list.stream().map(this::pidMatch).distinct().toList();
        List<String> distinct = this.distinctHeader(pidList);
        List<String> distinctDetailUrl = new ArrayList<>(CollUtil.disjunction(pidList, distinct));
        String oumei = "https://906524.xyz/html/movie/pc/oumei_";
        String html = ".html";
        List<String> clearList = distinctDetailUrl.stream().map(m -> oumei + m + html).toList();
        List<MgDetailPo> mgDetailPoList = new CopyOnWriteArrayList<>();
        List<MgPreviewPicDto> mgPreviewPicDtoList = new CopyOnWriteArrayList<>();
        for (String durl : clearList) {
            try {
                chromeDriver.get(durl);
                chromeDriver.manage().timeouts().implicitlyWait(100, TimeUnit.MILLISECONDS);
                WebElement content = chromeDriver.findElementByCssSelector("#content");
                List<WebElement> previewPicUrlList = content.findElements(By.cssSelector("div.capture > img"));
                String pid = pidMatch(durl);
                mgPreviewPicDtoList.addAll(this.parsePreviewPicUrl2Dto(previewPicUrlList, pid));
                mgDetailPoList.add(this.parse2Header(durl, content));
            } catch (Exception e) {
                e.printStackTrace();
                redisTemplate.opsForSet().add(MgConstant.MG_LIST_KEY + type, durl);
            }
        }
        return transactionTemplate.execute(status -> {
            mgHeaderRepository.saveAllAndFlush(mgDetailPoList);
            mgPreviewPicService.saveUpdateBatch(mgPreviewPicDtoList);
            betriceResultVo.result(BetriceResultCode.SUCCESS_200, "操作成功！");
            betriceResultVo.setHandleCount(mgDetailPoList.size());
            return betriceResultVo;
        });
    }

    /**
     * 根据网页采集的明细网址和一存在的网址做对比
     *
     * @param pidList 系统ID
     * @return 已存在的header对象
     */
    private List<String> distinctHeader(List<String> pidList) {
        LambdaQueryWrapper<MgDetailPo> queryWrapper = new QueryWrapper<MgDetailPo>().lambda();
        queryWrapper.in(MgDetailPo::getPid, pidList);
        return this.list(queryWrapper).stream().map(MgDetailPo::getPid).toList();
    }

    /**
     * 解析网页img元素，并转化为PreviewPicUrlDto
     *
     * @param previewPicUrlList img网页元素集合
     * @param pid               系统ID
     * @return 转化后的集合
     */
    private List<MgPreviewPicDto> parsePreviewPicUrl2Dto(List<WebElement> previewPicUrlList, String pid) {
        List<MgPreviewPicDto> mgPreviewPicDtoList = new ArrayList<>();
        for (WebElement previewPic : previewPicUrlList) {
            String picUrl = previewPic.getAttribute("src");
            MgPreviewPicDto mgPreviewPicDto = new MgPreviewPicDto();
            mgPreviewPicDto.setPid(pid);
            mgPreviewPicDto.setPicUrl(picUrl);
            mgPreviewPicDtoList.add(mgPreviewPicDto);
        }
        mgPreviewPicDtoList.sort((o1, o2) -> {
            String picUrl1 = o1.getPicUrl();
            String picUrl2 = o2.getPicUrl();
            return Integer.parseInt(picUrl1.substring(picUrl1.lastIndexOf("/") + 1, picUrl1.length() - 4)) -
                    Integer.parseInt(picUrl2.substring(picUrl2.lastIndexOf("/") + 1, picUrl2.length() - 4));

        });
        return mgPreviewPicDtoList;
    }

    /**
     * 解析网页元素为mgHeader
     *
     * @param durl    明细URL
     * @param content 父级标签
     * @return 组装好的Header对象
     */
    private MgDetailPo parse2Header(String durl, WebElement content) {
        String pid = pidMatch(durl);
        String title = content.findElement(By.cssSelector("h2")).getText();
        Date pubDate = Optional.ofNullable(content.findElement(By.cssSelector("p:nth-child(2)")).getText()).map(m -> {
            if (pubDateStr.length() < m.length()) {
                try {
                    return dateFormat.parse(m.substring(pubDateStr.length()));
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }).orElse(null);
        String mvForm = Optional.ofNullable(content.findElement(By.cssSelector("p:nth-child(3)")).getText()).map(m -> {
            if (pubDateStr.length() < m.length()) {
                return m.substring(pubDateStr.length());
            }
            return null;
        }).orElse(null);
        Integer mvSize = Optional.ofNullable(content.findElement(By.cssSelector("p:nth-child(4)")).getText()).map(m -> {
            if (pubDateStr.length() < m.length()) {
                return Integer.valueOf(m.substring(pubDateStr.length()).replace(" MB", ""));
            }
            return null;
        }).orElse(null);
        Integer mvDuration = Optional.ofNullable(content.findElement(By.cssSelector("p:nth-child(5)")).getText()).map(m -> {
            if (pubDateStr.length() < m.length()) {
                return Integer.valueOf(m.substring(pubDateStr.length()).replace("分钟", ""));
            }
            return null;
        }).orElse(null);
        String mvResolution = Optional.ofNullable(content.findElement(By.cssSelector("p:nth-child(6)")).getText()).map(m -> {
            if (pubDateStr.length() - 1 < m.length()) {
                return m.substring(pubDateStr.length() - 1);
            }
            return null;
        }).orElse(null);
        String downloadUrl = content.findElement(By.cssSelector("div.download > b > font > a")).getAttribute("href");
        String magnet = jsoupComponent.getBody(downloadUrl).select("div > p:nth-child(1)").text().substring(5);
        MgDetailPo mgDetailPo = new MgDetailPo();
        mgDetailPo.setPid(pid);
        mgDetailPo.setTitle(title);
        mgDetailPo.setPubDate(pubDate);
        mgDetailPo.setMvForm(mvForm);
        mgDetailPo.setMvSize(mvSize);
        mgDetailPo.setMvDuration(mvDuration);
        mgDetailPo.setMvResolution(mvResolution);
        mgDetailPo.setDetailUrl(durl);
        mgDetailPo.setMagnet(magnet);
        return mgDetailPo;
    }

    public String pidMatch(String url) {
        String regEx = "(?<=oumei_).*?(?=.html)";
        Pattern pattern = Pattern.compile(regEx);
        //用定义好的正则表达式拆分字符串，把字符串中的数字留出来
        Matcher matcher = pattern.matcher(url);
        String result = null;
        while (matcher.find()) {
            result = matcher.group();
        }
        return result;
    }
}
